package com.bgs.engineering.config;

import com.bgs.engineering.security.commonhandler.UserLoginAuthenticationFailureHandler;
import com.bgs.engineering.security.commonhandler.UserLoginAuthenticationSuccessHandler;
import com.bgs.engineering.security.customizelogin.email.EmailCodeAuthenticationFilter;
import com.bgs.engineering.security.customizelogin.email.EmailCodeAuthenticationProvider;
import com.bgs.engineering.security.customizelogin.phone.PhoneCodeAuthenticationFilter;
import com.bgs.engineering.security.customizelogin.phone.PhoneCodeAuthenticationProvider;
import com.bgs.engineering.security.customizelogin.usernamepassword.UsernamePasswordAuthenticationProvider;
import com.bgs.engineering.security.customizelogin.usernamepassword.UsernamePasswordFilter;
import com.bgs.engineering.security.customizelogin.usernamepasswordcode.UsernamePasswordCodeAuthenticationProvider;
import com.bgs.engineering.security.customizelogin.usernamepasswordcode.UsernamePasswordCodeFilter;
import com.bgs.engineering.security.jwt.ExceptionHandlerFilter;
import com.bgs.engineering.security.jwt.JWTAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
import java.util.Arrays;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * @ClassName WebSecurityConfig
 * @Description Security 权限配置
 * @Author zcy
 * @Date 2023/3/27 10:28
 **/
@Configuration
public class WebSecurityConfig {

  /**
   * 自定登录失败做出的返回结果
   */
  @Autowired
  AuthenticationEntryPoint authExceptionEntryPoint;
  /**
   * 自定义登出处理逻辑
   */
  @Autowired
  LogoutSuccessHandler logoutSuccessHandler;
  /**
   * 登录失败处理逻辑
   */
  @Autowired
  AuthenticationFailureHandler authenticationFailureHandler;
  /**
   * 登录成功的操作
   */
  @Autowired
  AuthenticationSuccessHandler authenticationSuccessHandler;
  /**
   * 自定义用户名密码验证码信息认证
   */
  @Resource
  UsernamePasswordCodeAuthenticationProvider usernamePasswordCodeAuthenticationProvider;
  /**
   * 自定义邮箱认证
   */
  @Resource
  EmailCodeAuthenticationProvider emailCodeAuthenticationProvider;
  /**
   * 自定义手机号认证
   */
  @Resource
  PhoneCodeAuthenticationProvider phoneCodeAuthenticationProvider;
  /**
   * 自定义 用户名密码认证
   */
  @Resource
  UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;
  /**
   * 登录失败处理逻辑
   */
  @Autowired
  UserLoginAuthenticationFailureHandler userLoginAuthenticationFailureHandler;

  /**
   * 自定义用户登录成功操作
   */
  @Resource
  UserLoginAuthenticationSuccessHandler userLoginAuthenticationSuccessHandler;

  /**
   * 捕获token异常fv
   */
  @Autowired
  ExceptionHandlerFilter exceptionHandlerFilter;

  /**
   * 声明密码加密方式
   *
   * @return
   */
  @Bean
  public BCryptPasswordEncoder bCryptPasswordEncoder() {
    return new BCryptPasswordEncoder();
  }

  /**
   * 放行路径
   */
  private static final String[] AUTH_WHITELIST = {
          // -- swagger ui
          "/swagger-resources/**",
          "/swagger-ui.html",
          "/swagger-ui/",
          "/v3/api-docs",
          "/v2/api-docs",
          "/swagger-ui/**",
          "/webjars/**",
          "/images/**",
          "/js/**",
          "/webjars/**",
          "/druid/**",
          "/sysUser/code",
          "/**"
  };

  /**
   * 注册 Security相关自定义过滤器
   *
   * @param http
   * @return
   * @throws Exception
   */
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeHttpRequests((auth) -> {
                      try {
                        auth.antMatchers(AUTH_WHITELIST).permitAll()
                                .anyRequest().authenticated()
                                //异常处理(权限拒绝、登录失效等)
                                .and().exceptionHandling().
                                authenticationEntryPoint(authExceptionEntryPoint)//匿名用户访问无权限资源时的异常处理
                                //登入
                                .and().formLogin().
                                loginProcessingUrl("/login").
                                permitAll().
                                failureHandler(authenticationFailureHandler).//登录失败处理逻辑
                                successHandler(authenticationSuccessHandler) //登录成功处理逻辑
                                // 登出
                                .and().logout().
                                permitAll().
                                logoutSuccessHandler(logoutSuccessHandler) //登出成功处理逻辑
                                .and().sessionManagement().
                                sessionCreationPolicy(SessionCreationPolicy.STATELESS)//禁用session
                                .and().cors().configurationSource(configurationSource()); // 允许跨域
                      } catch (Exception e) {
                        throw new RuntimeException(e);
                      }
                    }
            )
            .httpBasic(withDefaults());
    // 禁用缓存
    http.headers().cacheControl();
    // 添加JWT过滤器
    http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager(http)));
    // 注册异常捕获过滤器
    http.addFilterBefore(exceptionHandlerFilter, CorsFilter.class);
    // 注册密码登录
    http.addFilterAt(usernamePasswordFilter(http), UsernamePasswordAuthenticationFilter.class);
    // 注册用户名密码验证码登录
    http.addFilterAt(usernamePasswordCodeFilter(http), UsernamePasswordAuthenticationFilter.class);
    // 注册邮箱验证码登录
    http.addFilterAt(emailLoginFilter(http), UsernamePasswordAuthenticationFilter.class);
    // 注册手机号验证码登录
    http.addFilterAt(phoneCodeAuthenticationFilter(http), UsernamePasswordAuthenticationFilter.class);
    return http.build();
  }

  /**
   * 注册自定义登录
   *
   * @param httpSecurity
   * @return
   * @throws Exception
   */
  @Bean("authenticationManager")
  @Primary
  AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
    AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
            .build();
    return authenticationManager;
  }

  @Bean("authenticationManagerCode")
  AuthenticationManager authenticationManagerCode(HttpSecurity httpSecurity) throws Exception {
    AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
            .authenticationProvider(usernamePasswordCodeAuthenticationProvider)
            .build();
    return authenticationManager;
  }

  @Bean("authenticationManagerPassword")
  AuthenticationManager authenticationManagerPassword(HttpSecurity httpSecurity) throws Exception {
    AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
            .authenticationProvider(usernamePasswordAuthenticationProvider)
            .build();
    return authenticationManager;
  }

  @Bean("authenticationManagerEmail")
  AuthenticationManager authenticationManagerEmail(HttpSecurity httpSecurity) throws Exception {
    AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
            .authenticationProvider(emailCodeAuthenticationProvider)
            .build();
    return authenticationManager;
  }

  @Bean("authenticationManagerPhone")
  AuthenticationManager authenticationManagerPhone(HttpSecurity httpSecurity) throws Exception {
    AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
            .authenticationProvider(phoneCodeAuthenticationProvider)
            .build();
    return authenticationManager;
  }

  /**
   * 注册自定义用户 密码 验证码 登录拦截器
   *
   * @param http
   * @return
   * @throws Exception
   */
  @Bean
  public UsernamePasswordCodeFilter usernamePasswordCodeFilter(HttpSecurity http) throws Exception {
    UsernamePasswordCodeFilter usernamePasswordCodeFilter = new UsernamePasswordCodeFilter("/userCode/login");
    usernamePasswordCodeFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
    usernamePasswordCodeFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
    usernamePasswordCodeFilter.setAuthenticationManager(authenticationManagerCode(http));
    return usernamePasswordCodeFilter;
  }

  /**
   * 注册自定义 用户 密码 登录拦截器
   *
   * @param http
   * @return
   * @throws Exception
   */
  @Bean
  public UsernamePasswordFilter usernamePasswordFilter(HttpSecurity http) throws Exception {
    UsernamePasswordFilter usernamePasswordFilter = new UsernamePasswordFilter("/user/login");
    usernamePasswordFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
    usernamePasswordFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
    usernamePasswordFilter.setAuthenticationManager(authenticationManagerPassword(http));
    return usernamePasswordFilter;
  }

  /**
   * 注册邮箱 验证码 登录拦截器
   *
   * @return
   * @return
   * @throws Exception
   */
  @Bean
  public EmailCodeAuthenticationFilter emailLoginFilter(HttpSecurity http) throws Exception {
    EmailCodeAuthenticationFilter emailLoginFilter = new EmailCodeAuthenticationFilter("/email/login");
    emailLoginFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
    emailLoginFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
    emailLoginFilter.setAuthenticationManager(authenticationManagerEmail(http));
    return emailLoginFilter;
  }

  /**
   * 注册手机号 验证码 登录拦截器
   *
   * @return
   * @return
   * @throws Exception
   */
  @Bean
  public PhoneCodeAuthenticationFilter phoneCodeAuthenticationFilter(HttpSecurity http) throws Exception {
    PhoneCodeAuthenticationFilter phoneCodeAuthenticationFilter = new PhoneCodeAuthenticationFilter("/phone/login");
    phoneCodeAuthenticationFilter.setAuthenticationSuccessHandler(userLoginAuthenticationSuccessHandler);
    phoneCodeAuthenticationFilter.setAuthenticationFailureHandler(userLoginAuthenticationFailureHandler);
    phoneCodeAuthenticationFilter.setAuthenticationManager(authenticationManagerPhone(http));
    return phoneCodeAuthenticationFilter;
  }

  /**
   * @Description 允许跨域
   * @Author lzy
   * @Date 2023/4/8 11:31
   * @Return org.springframework.web.cors.CorsConfigurationSource
   */
  CorsConfigurationSource configurationSource() {
    CorsConfiguration corsConfiguration = new CorsConfiguration();
    corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
    corsConfiguration.setAllowedMethods(Arrays.asList("*"));
    corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
    corsConfiguration.setMaxAge(3600L);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", corsConfiguration);
    return source;
  }
}
